/*
 * File:   BlockLinkedList.cpp
 * Author: Usama, Max, Andy
 *
 * Created on February 4, 2011, 10:17 AM
 */


// LastBlock.nextPtr will be -1 for the list
#include "BlockLinkedList.h"
#include "FreeList.h"
/**
 * Default initialization for all data members. Does not modify the Disk.
 */
BlockLinkedList::BlockLinkedList() {
    m_disk = NULL;
    //amount of usable data = empty space in block?
    // refers to block so will be tied to block pointed in the list
    m_blockSize = Disk::DEFAULT_BLOCK_SIZE - 4;
    m_numberOfBlocks = 0;
    m_startBlockNumber = -1;
    m_endBlockNumber = -1;
    m_seek = NULL;
}

BlockLinkedList::BlockLinkedList(Disk* disk, int blockSize){
    m_disk = disk;
    m_blockSize = blockSize;
    m_numberOfBlocks = 0;
    m_startBlockNumber = -1;
    m_endBlockNumber = -1;
    m_seek = NULL;

}
BlockLinkedList::BlockLinkedList(BlockLinkedList* b) {
    m_disk = b->m_disk;
    m_blockSize = b->m_blockSize;
    m_numberOfBlocks = b->m_numberOfBlocks;
    m_startBlockNumber = b->m_startBlockNumber;
    m_endBlockNumber = b->m_endBlockNumber;
    m_seek = b->m_seek;
}

bool BlockLinkedList::Initialize(int blockNumber){
    printf("BlockLinkedList::Initialize: ");
    bool success = false;
    m_numberOfBlocks = 1;
    m_blockSize = Disk::DEFAULT_BLOCK_SIZE - 4;
    m_startBlockNumber = blockNumber;
    m_endBlockNumber = blockNumber;
    Block nBlk = Block(blockNumber,m_disk);
    nBlk.ClearBuffer();
    nBlk.SetPointer(-1,0);

    success = nBlk.Write(m_disk);
    delete m_seek;
    m_seek = new Block(m_startBlockNumber,m_disk);

    return success;
}

int BlockLinkedList::GetBlockSize(){
    return m_blockSize;
}

int BlockLinkedList::GetNumberOfBlocks(){
    return m_numberOfBlocks;
}

Block* BlockLinkedList::UnlinkBlock(){
    int secondBlock;
    Block*  first = new Block(m_startBlockNumber,m_disk);
    secondBlock = first->GetPointer(0);
    if(m_seek->GetBlockNumber()==m_startBlockNumber)GetNextBlock();
    m_startBlockNumber = secondBlock;

    m_numberOfBlocks--;
    first->ClearBuffer();
    first->SetPointer(-1,0);
    first->Write(m_disk);
    return first;
}

bool BlockLinkedList::AddBlock(Block* newBlock){
    printf("BlockLinkedList::AddBlock: ");
    bool success = false;
    int nBlkNum = newBlock->GetBlockNumber();//the new endBlockNumber

    Block lastBlock = Block(m_endBlockNumber,m_disk);
    lastBlock.SetPointer(nBlkNum,0);//sets lastBlock = secondLastBlock;
    m_endBlockNumber = nBlkNum;
    success = lastBlock.Write(m_disk);//writes to disk
    if(success){//set newBlock.nextPtr = -1
        newBlock->SetPointer(-1,0);
        success = newBlock->Write(m_disk);
    }
    if(!success) printf("Unable to AddBlock\n.");
    //else printf("Added Block %s",newBlock->GetBuffer());
    m_numberOfBlocks++;

    //Check if m_seek = secondLastBlock. if true, update its pointer
    if(m_seek->GetBlockNumber() == lastBlock.GetBlockNumber())
        m_seek->SetPointer(m_endBlockNumber, 0);
    return success;
}

int BlockLinkedList::GetStartBlockNumber(){
    return m_startBlockNumber;
}
int BlockLinkedList::GetEndBlockNumber(){
    return m_endBlockNumber;
}

void BlockLinkedList::Rewind(){
    delete m_seek;
    m_seek = new Block(m_startBlockNumber,m_disk);
}

Block* BlockLinkedList::GetCurrentBlock(){
    return m_seek;
}

void BlockLinkedList::GetNextBlock(){
    if(m_seek!=NULL){
    int nextBlk = m_seek->GetPointer(0);
    delete m_seek;
    m_seek = NULL;
    if(nextBlk == -1) return;
    else m_seek = new Block(nextBlk,m_disk);
    }
}

void BlockLinkedList::Output(){
    printf("BlockLinkedList::Output: ");
    if(m_numberOfBlocks == 0){
        printf("No blocks in BlockLinkedList\n");
        return;
    }
    Rewind();
    printf("firstBlock = %d",m_seek->GetBlockNumber());
    int next = m_seek->GetPointer(0);
    while(next!= m_endBlockNumber && next!=-1)
    {
        printf(", %d",next);
        GetNextBlock();
        next = m_seek->GetPointer(0);
    }
    printf(", lastBlock = %d\n",m_endBlockNumber);
}
bool BlockLinkedList::Replace(Block *blk){
    printf("BlockLinkedList::Replace: ");
    bool success = false;
    if(m_seek == NULL){
        printf("Current Block is NULL\n");
        return false;
    }

   // blk->SetBlockNumber(m_seek->GetBlockNumber());
    blk->SetPointer(m_seek->GetPointer(0),0);
    m_seek->SetBuffer(blk->GetBuffer());
//    success = blk->Write((m_disk));
    printf(" Contents: ",m_seek->GetBuffer());
    success = m_seek->Write(m_disk);
    if(success) printf("Success\n"); else printf("Failed!\n");
    return success;
}

int BlockLinkedList::CountBlocks(){
    printf(" BlockLinkedList:: CountBlocks: ");
    Rewind();
    int count = 0;
    if(m_startBlockNumber == -1) count = 0;
    else if(m_endBlockNumber == m_seek->GetBlockNumber())
        count = 1;
    else {
        do{
        count++;
        GetNextBlock();
        }while(m_seek!= NULL);
        
    }
    return count;
}

Disk* BlockLinkedList::GetDisk(){
    return m_disk;
}


BlockLinkedList::~BlockLinkedList() {
    delete m_seek;
}

/*
char* charToInt(int data){
    int*p;
    p = &data;
    char *s = (char*) p;
    for(int i = 0; i<sizeof(int); i++){
        s[i] = data[i];}
    return s;

}
int intToChar(char* data){

   
    int*p;
    int buff = 0;
    p = &buff;
    char *s = (char*) p;
    for(int i = 0; i<sizeof(int); i++){
        s[i] = data[i];}
    return buff;
}//*/

void BlockLinkedList::Test() {
//        char *s = NULL;
//       char *blockName = "Block1";
//        int numberOfBlocks = 1000;
        int cmd = 0;
        Disk* disk1 = new Disk("Disk1.disk",FreeList::DEFAULT_NUMBER_OF_BLOCKS,Disk::DEFAULT_BLOCK_SIZE);
        //disk1->Format();
//        Block test(27, 512);
//        unsigned char m_buffer[Disk::DEFAULT_BLOCK_SIZE];
//        int blockNumber = 27;
//        int byteToWrite;
//        unsigned char *block = new unsigned char[Disk::DEFAULT_BLOCK_SIZE];
        printf("Succesfully opened and formatted disk1\n");

//        unsigned char buff[Disk::DEFAULT_BLOCK_SIZE];
//        unsigned char wrt[Disk::DEFAULT_BLOCK_SIZE] = "Random Stuff\0";
//        if(disk1->WriteBlock(0,wrt))
//            printf("wrt written\n");
//
//        if(disk1->ReadBlock(0,buff))
//            printf("read mother block: %s!\n",buff);
//        else printf("Can't read mother block!\n");
//        return;
        Block mb = Block(0,Disk::DEFAULT_BLOCK_SIZE);
        //printf("Mother Block read!\n");
        mb.ClearBuffer();
        unsigned char m_buffer[Disk::DEFAULT_BLOCK_SIZE] = "abcdesfefhohfeohfoehgdgrejpoofjsopjfshyfois\0";
        //mb.SetBlockNumber(0);
        mb.SetBuffer(m_buffer);
        mb.SetPointer(1,0);
        mb.SetPointer(4,1);
        mb.SetPointer(4,2);
        //mb.SetPointer(124214,6);
       // unsigned char * abd;// = unsigned char *)"abcdef";
        //printf("%s\n",abd);
        //mb.Print();
//        printf(" ptr @0: %d\n",mb.GetPointer(0));
//        printf(" ptr @1: %d\n",mb.GetPointer(1));
//        printf(" ptr @2: %d\n",mb.GetPointer(2));
//        printf("MotherBlock : %s\n",mb.GetBuffer());
        bool success = mb.Write(disk1);
//        abd = mb.GetBuffer();
//        if(success) printf("MotherBlock written %c %c\n",abd[3],abd[5]);
//        //return;
//
//        Block read = Block(0,disk1);
//        printf("Read from mb: %s\n",read.GetBuffer());
//        printf(" ptr@ 0: %d\n",read.GetPointer(0));
//        disk1->Close();
//        exit(1);

        Block b = Block(1,Disk::DEFAULT_BLOCK_SIZE);
//        unsigned char* content = (unsigned char*) "    Block 1 ";
//        b.SetBuffer(content);
        for(int i =1; i< 5; i++)
        {
            
            b.SetBlockNumber(i);
            b.SetPointer(i+1,0);
            b.Write(disk1);
            b.ClearBuffer();
            //printf("Wrote block number %d\n",i);
        }
        //lastBlock
        b.SetPointer(-1,0);
        b.SetBlockNumber(4);
        b.Write(disk1);

        //Initialize i.e.
        BlockLinkedList bll(disk1,Disk::DEFAULT_BLOCK_SIZE);
        b.SetBuffer((unsigned char*)"Abracadaebra. Anaconda. Alliphant."
        " Astronaut astonished by Alien Anarchy");
        

        //To Add 2nd block
       // b.SetBuffer((unsigned char*)"     Block 2");
        b.SetBlockNumber(2);
        Block* replace = new Block(4,disk1);
        replace->SetBuffer((unsigned char*)"         Block 4");
        


        for (;;) {
            printf("\nmain: Enter command:\n");
            printf("   0) Exit program\n");
            printf("   1) getDisk\n");
            printf("   2) initialize\n");
            printf("   3) addBlock\n");
            printf("   4) CountBlocks()\n");
            printf("   5) getStartBlockNumber, getEndBlockNumber\n");
            printf("   6) GetNextPtr\n");
            printf("   7) getCurrentBlock\n");
            printf("   8) Rewind\n");
            printf("   9) Replace\n");
            printf("  10) Output()\n");

            printf("Enter command: ");
            scanf("%d", &cmd);

            switch (cmd) {
                case 0:
                    exit(1);
                    break;

                case 1: // getDisk
                    if(bll.GetDisk() != NULL)
                        printf("Got Disk*!\n");
                    break;
                case 2: // Initialize
                   if(bll.Initialize(1))
                       printf(" Success!\n");
                      break;
                case 3: // addBlock
                    if(bll.AddBlock(&b))
                        printf("Successfully added blk number %d\n",b.GetBlockNumber());

                      break;
                case 4: // CountBlocks
                    printf(" (Count = %d) = (%d: datamember count) blocks in "
                            "list.\n",bll.CountBlocks(),bll.GetNumberOfBlocks());
                      break;
                case 5: // getStartBlockNumber
                    printf(" Start Block Number: %d\n",bll.GetStartBlockNumber());
                     printf(" End Block Number: %d\n",bll.GetEndBlockNumber());
                      break;
                case 6: // GetNextBlk
                    bll.GetNextBlock();
                    if(bll.GetCurrentBlock() == NULL)
                        printf(" Next Block is NULL");
                    else printf("Next Block: %d",bll.GetCurrentBlock()->GetBlockNumber());
                    break;
                case 7: // getCurrentBlock
                  printf(" Current Block: %d. Its Contents: %s",
                          bll.GetCurrentBlock()->GetBlockNumber(),
                          bll.GetCurrentBlock()->GetBuffer());
                    break;
                case 8: // Rewind
                  bll.Rewind();
                  printf(" : %d\n",bll.GetCurrentBlock()->GetBlockNumber());
                    break;
                case 9: // Replace
                    printf("Replacing %d with %d: , new Contents: %s\n "
                            ,bll.GetCurrentBlock()->GetBlockNumber(),
                            replace->GetBlockNumber(),replace->GetBuffer());
                    if(bll.Replace(replace))
                        printf(" Success\n");
                    break;
                case 10: //Output
                    bll.Output();
                    break;

                default:
                    printf("main: Bad command!\n");
                     break;
            } // end switch
        } // end for
        delete replace;
    } // end Test()
